import sys
import os
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTabWidget, QWidget, QVBoxLayout,
                             QHBoxLayout, QLabel, QPushButton, QFileDialog, QSlider,
                             QSpinBox, QComboBox, QGroupBox, QGridLayout, QMessageBox,
                             QSplitter, QAction, QToolBar, QStatusBar, QDockWidget,
                             QDoubleSpinBox, QCheckBox, QScrollArea, QFrame)
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QColor, QFont
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QPoint, QRect, QSize
from PyQt5.QtSvg import QSvgWidget

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import time
import threading

# 设置中文字体支持
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]


class ImageProcessor:
    """图像处理核心类，负责所有图像处理算法的实现"""

    def __init__(self):
        self.original_image = None
        self.processed_image = None
        self.current_image = None
        self.faces = []
        self.shapes = []
        self.crop_rect = None
        self.image_history = []
        self.history_index = -1

    def load_image(self, file_path):
        """加载图像"""
        self.original_image = cv2.imread(file_path)
        if self.original_image is None:
            return False
        self.processed_image = self.original_image.copy()
        self.current_image = self.original_image.copy()
        self._reset_history()
        return True

    def save_image(self, file_path):
        """保存当前处理的图像"""
        if self.current_image is not None:
            cv2.imwrite(file_path, self.current_image)
            return True
        return False

    def rotate_image(self, angle):
        """旋转图像"""
        if self.current_image is None:
            return

        (h, w) = self.current_image.shape[:2]
        center = (w // 2, h // 2)
        M = cv2.getRotationMatrix2D(center, angle, 1.0)
        self.processed_image = cv2.warpAffine(self.current_image, M, (w, h))
        self._update_current_image()

    def resize_image(self, scale_percent):
        """缩放图像"""
        if self.current_image is None:
            return

        width = int(self.current_image.shape[1] * scale_percent / 100)
        height = int(self.current_image.shape[0] * scale_percent / 100)
        dim = (width, height)
        self.processed_image = cv2.resize(self.current_image, dim, interpolation=cv2.INTER_AREA)
        self._update_current_image()

    def crop_image(self, rect):
        """裁剪图像"""
        if self.current_image is None:
            return

        x, y, w, h = rect
        if w > 0 and h > 0:
            self.processed_image = self.current_image[y:y + h, x:x + w]
            self._update_current_image()

    def adjust_brightness_contrast(self, brightness=0, contrast=1.0):
        """调整亮度和对比度"""
        if self.current_image is None:
            return

        # 确保输入值在有效范围内
        brightness = max(-255, min(255, brightness))
        contrast = max(0.1, min(3.0, contrast))

        # 应用亮度和对比度调整
        if brightness != 0:
            if self.current_image.dtype == np.uint8:
                # 对于8位图像，使用cv2.addWeighted
                self.processed_image = cv2.addWeighted(self.current_image, contrast,
                                                       np.zeros(self.current_image.shape, self.current_image.dtype),
                                                       0, brightness)
            else:
                # 对于浮点图像，手动调整
                self.processed_image = np.clip(self.current_image * contrast + brightness, 0, 255).astype(
                    self.current_image.dtype)
        else:
            self.processed_image = np.clip(self.current_image * contrast, 0, 255).astype(self.current_image.dtype)

        self._update_current_image()

    def adjust_saturation(self, saturation_factor):
        """调整饱和度"""
        if self.current_image is None:
            return

        # 转换为HSV颜色空间
        hsv = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2HSV)

        # 调整饱和度通道
        hsv[:, :, 1] = np.clip(hsv[:, :, 1] * saturation_factor, 0, 255).astype(hsv.dtype)

        # 转回BGR颜色空间
        self.processed_image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
        self._update_current_image()

    def apply_smoothing(self, method, kernel_size=3):
        """应用平滑处理"""
        if self.current_image is None:
            return

        if method == '均值滤波':
            self.processed_image = cv2.blur(self.current_image, (kernel_size, kernel_size))
        elif method == '高斯滤波':
            self.processed_image = cv2.GaussianBlur(self.current_image, (kernel_size, kernel_size), 0)
        elif method == '中值滤波':
            self.processed_image = cv2.medianBlur(self.current_image, kernel_size)
        elif method == '双边滤波':
            self.processed_image = cv2.bilateralFilter(self.current_image, 9, 75, 75)

        self._update_current_image()

    def detect_shapes(self):
        """检测图像中的几何形状"""
        if self.current_image is None:
            return []

        # 转换为灰度图
        gray = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2GRAY)

        # 高斯模糊以减少噪声
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)

        # 边缘检测
        edged = cv2.Canny(blurred, 50, 150)

        # 查找轮廓
        contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        self.shapes = []
        output = self.current_image.copy()

        # 分析每个轮廓
        for contour in contours:
            # 计算轮廓周长
            perimeter = cv2.arcLength(contour, True)

            # 多边形逼近
            approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)

            # 获取边界框
            x, y, w, h = cv2.boundingRect(approx)

            # 根据顶点数量确定形状
            if len(approx) == 3:
                shape_name = "三角形"
                color = (0, 255, 0)  # 绿色
            elif len(approx) == 4:
                # 计算宽高比判断是正方形还是矩形
                aspect_ratio = float(w) / h
                shape_name = "正方形" if aspect_ratio >= 0.95 and aspect_ratio <= 1.05 else "矩形"
                color = (0, 0, 255)  # 蓝色
            elif len(approx) > 4:
                # 计算轮廓面积和外接圆面积之比判断是圆形还是多边形
                area = cv2.contourArea(contour)
                ((x_c, y_c), radius) = cv2.minEnclosingCircle(contour)
                circle_area = np.pi * radius * radius
                circularity = 4 * np.pi * area / (perimeter * perimeter)

                if circularity > 0.8:
                    shape_name = "圆形"
                    color = (255, 0, 0)  # 红色
                else:
                    shape_name = "多边形"
                    color = (255, 255, 0)  # 黄色
            else:
                shape_name = "未知"
                color = (0, 0, 0)  # 黑色

            # 记录形状信息
            self.shapes.append({
                'name': shape_name,
                'contour': contour,
                'approx': approx,
                'bounding_box': (x, y, w, h),
                'color': color
            })

            # 在图像上绘制轮廓和形状名称
            cv2.drawContours(output, [contour], -1, color, 2)
            cv2.putText(output, shape_name, (x, y - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

        self.processed_image = output
        self._update_current_image()
        return self.shapes

    def detect_faces(self):
        """检测图像中的人脸"""
        if self.current_image is None:
            return []

        # 将BGR格式转换为RGB（face_recognition库使用RGB格式）
        rgb_image = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2RGB)

        # 检测人脸位置
        face_locations = face_recognition.face_locations(rgb_image)

        # 检测人脸特征点
        face_landmarks = face_recognition.face_landmarks(rgb_image)

        self.faces = []
        output = self.current_image.copy()

        # 在图像上标记人脸
        for (top, right, bottom, left), landmarks in zip(face_locations, face_landmarks):
            # 绘制人脸边界框
            cv2.rectangle(output, (left, top), (right, bottom), (0, 255, 0), 2)

            # 绘制面部特征点
            for feature_name, points in landmarks.items():
                for point in points:
                    cv2.circle(output, point, 2, (0, 0, 255), -1)

            # 记录人脸信息
            self.faces.append({
                'location': (left, top, right, bottom),
                'landmarks': landmarks
            })

        self.processed_image = output
        self._update_current_image()
        return self.faces

    def stitch_images(self, images):
        """拼接多张图像"""
        if len(images) < 2:
            return False

        # 初始化图像拼接器
        stitcher = cv2.Stitcher_create() if cv2.__version__.startswith('4') else cv2.createStitcher()

        # 拼接图像
        status, result = stitcher.stitch(images)

        if status == cv2.STITCHER_OK:
            self.processed_image = result
            self._update_current_image()
            return True
        else:
            print(f"拼接失败，状态码: {status}")
            return False

    def _update_current_image(self):
        """更新当前图像并保存到历史记录"""
        self.current_image = self.processed_image.copy()
        self._save_to_history()

    def _save_to_history(self):
        """保存当前图像到历史记录"""
        # 如果在历史记录中间进行了新操作，则删除后续的历史记录
        if self.history_index < len(self.image_history) - 1:
            self.image_history = self.image_history[:self.history_index + 1]

        # 限制历史记录长度
        if len(self.image_history) >= 10:
            self.image_history.pop(0)
        else:
            self.history_index += 1

        self.image_history.append(self.current_image.copy())

    def _reset_history(self):
        """重置历史记录"""
        self.image_history = [self.current_image.copy()]
        self.history_index = 0

    def undo(self):
        """撤销上一步操作"""
        if self.history_index > 0:
            self.history_index -= 1
            self.current_image = self.image_history[self.history_index].copy()
            return True
        return False

    def redo(self):
        """重做上一步操作"""
        if self.history_index < len(self.image_history) - 1:
            self.history_index += 1
            self.current_image = self.image_history[self.history_index].copy()
            return True
        return False


class VideoProcessor(QThread):
    """视频处理线程，用于处理视频流"""

    frame_updated = pyqtSignal(np.ndarray)
    processing_finished = pyqtSignal()
    error_occurred = pyqtSignal(str)

    def __init__(self, source=0, processor=None):
        super().__init__()
        self.source = source
        self.processor = processor
        self.cap = None
        self.running = False
        self.processing = False
        self.detection_type = 'none'  # 'none', 'face', 'shape'

    def run(self):
        """线程运行函数"""
        try:
            self.cap = cv2.VideoCapture(self.source)

            if not self.cap.isOpened():
                self.error_occurred.emit(f"无法打开视频源: {self.source}")
                return

            self.running = True

            while self.running:
                ret, frame = self.cap.read()

                if not ret:
                    self.error_occurred.emit("无法读取视频帧")
                    break

                # 处理帧
                if self.processing:
                    if self.detection_type == 'face':
                        frame = self._process_face_detection(frame)
                    elif self.detection_type == 'shape':
                        frame = self._process_shape_detection(frame)

                # 发射更新信号
                self.frame_updated.emit(frame)

                # 控制帧率
                self.msleep(30)

        except Exception as e:
            self.error_occurred.emit(f"处理视频时出错: {str(e)}")
        finally:
            self.stop()
            self.processing_finished.emit()

    def stop(self):
        """停止线程"""
        self.running = False
        if self.cap and self.cap.isOpened():
            self.cap.release()

    def start_processing(self, detection_type):
        """开始处理视频帧"""
        self.processing = True
        self.detection_type = detection_type

    def stop_processing(self):
        """停止处理视频帧"""
        self.processing = False
        self.detection_type = 'none'

    def _process_face_detection(self, frame):
        """处理人脸检测"""
        # 将BGR格式转换为RGB
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # 检测人脸位置
        face_locations = face_recognition.face_locations(rgb_frame)

        # 检测人脸特征点
        face_landmarks = face_recognition.face_landmarks(rgb_frame)

        # 在图像上标记人脸
        for (top, right, bottom, left), landmarks in zip(face_locations, face_landmarks):
            # 绘制人脸边界框
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)

            # 绘制面部特征点
            for feature_name, points in landmarks.items():
                for point in points:
                    cv2.circle(frame, point, 2, (0, 0, 255), -1)

        return frame

    def _process_shape_detection(self, frame):
        """处理形状检测"""
        # 转换为灰度图
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 高斯模糊以减少噪声
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)

        # 边缘检测
        edged = cv2.Canny(blurred, 50, 150)

        # 查找轮廓
        contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 分析每个轮廓
        for contour in contours:
            # 计算轮廓周长
            perimeter = cv2.arcLength(contour, True)

            # 多边形逼近
            approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)

            # 获取边界框
            x, y, w, h = cv2.boundingRect(approx)

            # 根据顶点数量确定形状
            if len(approx) == 3:
                shape_name = "三角形"
                color = (0, 255, 0)  # 绿色
            elif len(approx) == 4:
                # 计算宽高比判断是正方形还是矩形
                aspect_ratio = float(w) / h
                shape_name = "正方形" if aspect_ratio >= 0.95 and aspect_ratio <= 1.05 else "矩形"
                color = (0, 0, 255)  # 蓝色
            elif len(approx) > 4:
                # 计算轮廓面积和外接圆面积之比判断是圆形还是多边形
                area = cv2.contourArea(contour)
                ((x_c, y_c), radius) = cv2.minEnclosingCircle(contour)
                circle_area = np.pi * radius * radius
                circularity = 4 * np.pi * area / (perimeter * perimeter)

                if circularity > 0.8:
                    shape_name = "圆形"
                    color = (255, 0, 0)  # 红色
                else:
                    shape_name = "多边形"
                    color = (255, 255, 0)  # 黄色
            else:
                shape_name = "未知"
                color = (0, 0, 0)  # 黑色

            # 在图像上绘制轮廓和形状名称
            cv2.drawContours(frame, [contour], -1, color, 2)
            cv2.putText(frame, shape_name, (x, y - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

        return frame


class ImageView(QScrollArea):
    """图像显示区域，支持缩放和平移"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWidgetResizable(True)
        self.setBackgroundRole(QPalette.Dark)

        self.image_label = QLabel()
        self.image_label.setBackgroundRole(QPalette.Dark)
        self.image_label.setScaledContents(True)

        self.setWidget(self.image_label)

        self.scale_factor = 1.0
        self.drag_start = None
        self.last_pos = QPoint()

    def set_image(self, image):
        """设置要显示的图像"""
        self.image_label.setPixmap(image)

    def scale_image(self, factor):
        """缩放图像"""
        self.scale_factor *= factor
        self.image_label.resize(self.scale_factor * self.image_label.pixmap().size())

        # 调整滚动条位置，保持缩放中心
        self.adjust_scrollbars(factor)

    def adjust_scrollbars(self, factor):
        """调整滚动条位置"""
        h_bar = self.horizontalScrollBar()
        v_bar = self.verticalScrollBar()

        old_pos = QPoint(h_bar.value() + self.viewport().width() / 2,
                         v_bar.value() + self.viewport().height() / 2)

        self.image_label.resize(self.scale_factor * self.image_label.pixmap().size())

        new_pos = QPoint(int(old_pos.x() * factor), int(old_pos.y() * factor))

        h_bar.setValue(new_pos.x() - self.viewport().width() / 2)
        v_bar.setValue(new_pos.y() - self.viewport().height() / 2)

    def wheelEvent(self, event):
        """鼠标滚轮事件 - 用于缩放"""
        if event.angleDelta().y() > 0:
            self.scale_image(1.1)
        else:
            self.scale_image(0.9)

    def mousePressEvent(self, event):
        """鼠标按下事件 - 用于拖动"""
        if event.button() == Qt.LeftButton:
            self.drag_start = event.pos()
            self.last_pos = self.mapToGlobal(event.pos())

    def mouseMoveEvent(self, event):
        """鼠标移动事件 - 用于拖动"""
        if event.buttons() & Qt.LeftButton and self.drag_start is not None:
            current_pos = self.mapToGlobal(event.pos())
            diff = current_pos - self.last_pos

            h_bar = self.horizontalScrollBar()
            v_bar = self.verticalScrollBar()

            h_bar.setValue(h_bar.value() - diff.x())
            v_bar.setValue(v_bar.value() - diff.y())

            self.last_pos = current_pos

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        self.drag_start = None


class MainWindow(QMainWindow):
    """主窗口类"""

    def __init__(self):
        super().__init__()

        # 设置中文字体
        font = QFont("SimHei")
        self.setFont(font)

        # 初始化图像处理类
        self.processor = ImageProcessor()

        # 初始化UI
        self.init_ui()

        # 状态变量
        self.is_cropping = False
        self.crop_start = None
        self.crop_end = None
        self.current_tab = 'image'  # 'image' 或 'video'

    def init_ui(self):
        """初始化用户界面"""
        # 设置窗口标题和大小
        self.setWindowTitle("计算机视觉开发平台")
        self.resize(1200, 800)

        # 创建菜单栏
        self.create_menu_bar()

        # 创建工具栏
        self.create_tool_bar()

        # 创建主部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # 创建主布局
        main_layout = QHBoxLayout(central_widget)

        # 创建分割器
        splitter = QSplitter(Qt.Horizontal)

        # 创建左侧控制面板
        control_panel = self.create_control_panel()
        splitter.addWidget(control_panel)

        # 创建右侧显示区域
        display_area = self.create_display_area()
        splitter.addWidget(display_area)

        # 设置分割器初始大小
        splitter.setSizes([300, 900])

        # 添加分割器到主布局
        main_layout.addWidget(splitter)

        # 创建状态栏
        self.statusBar().showMessage("就绪")

        # 连接信号和槽
        self.connect_signals()

    def create_menu_bar(self):
        """创建菜单栏"""
        menu_bar = self.menuBar()

        # 文件菜单
        file_menu = menu_bar.addMenu("文件")

        # 打开图像动作
        open_image_action = QAction("打开图像", self)
        open_image_action.setShortcut("Ctrl+O")
        open_image_action.triggered.connect(self.open_image)
        file_menu.addAction(open_image_action)

        # 打开视频动作
        open_video_action = QAction("打开视频", self)
        open_video_action.setShortcut("Ctrl+V")
        open_video_action.triggered.connect(self.open_video)
        file_menu.addAction(open_video_action)

        # 使用摄像头动作
        use_camera_action = QAction("使用摄像头", self)
        use_camera_action.setShortcut("Ctrl+C")
        use_camera_action.triggered.connect(self.use_camera)
        file_menu.addAction(use_camera_action)

        file_menu.addSeparator()

        # 保存图像动作
        save_image_action = QAction("保存图像", self)
        save_image_action.setShortcut("Ctrl+S")
        save_image_action.triggered.connect(self.save_image)
        file_menu.addAction(save_image_action)

        file_menu.addSeparator()

        # 退出动作
        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menu_bar.addMenu("编辑")

        # 撤销动作
        undo_action = QAction("撤销", self)
        undo_action.setShortcut("Ctrl+Z")
        undo_action.triggered.connect(self.undo)
        edit_menu.addAction(undo_action)

        # 重做动作
        redo_action = QAction("重做", self)
        redo_action.setShortcut("Ctrl+Y")
        redo_action.triggered.connect(self.redo)
        edit_menu.addAction(redo_action)

        edit_menu.addSeparator()

        # 复制动作
        copy_action = QAction("复制", self)
        copy_action.setShortcut("Ctrl+C")
        edit_menu.addAction(copy_action)

        # 粘贴动作
        paste_action = QAction("粘贴", self)
        paste_action.setShortcut("Ctrl+V")
        edit_menu.addAction(paste_action)

        # 视图菜单
        view_menu = menu_bar.addMenu("视图")

        # 放大动作
        zoom_in_action = QAction("放大", self)
        zoom_in_action.setShortcut("Ctrl++")
        zoom_in_action.triggered.connect(lambda: self.image_view.scale_image(1.1))
        view_menu.addAction(zoom_in_action)

        # 缩小动作
        zoom_out_action = QAction("缩小", self)
        zoom_out_action.setShortcut("Ctrl+-")
        zoom_out_action.triggered.connect(lambda: self.image_view.scale_image(0.9))
        view_menu.addAction(zoom_out_action)

        # 重置缩放动作
        reset_zoom_action = QAction("重置缩放", self)
        reset_zoom_action.setShortcut("Ctrl+0")
        reset_zoom_action.triggered.connect(self.reset_zoom)
        view_menu.addAction(reset_zoom_action)

        # 工具菜单
        tools_menu = menu_bar.addMenu("工具")

        # 形状识别动作
        shape_detection_action = QAction("形状识别", self)
        shape_detection_action.triggered.connect(self.detect_shapes)
        tools_menu.addAction(shape_detection_action)

        # 人脸识别动作
        face_detection_action = QAction("人脸识别", self)
        face_detection_action.triggered.connect(self.detect_faces)
        tools_menu.addAction(face_detection_action)

        # 帮助菜单
        help_menu = menu_bar.addMenu("帮助")

        # 使用说明动作
        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

    def create_tool_bar(self):
        """创建工具栏"""
        tool_bar = QToolBar("工具栏", self)
        self.addToolBar(tool_bar)

        # 添加工具按钮
        # 这里可以添加常用工具的图标按钮

    def create_control_panel(self):
        """创建控制面板"""
        control_widget = QWidget()
        control_layout = QVBoxLayout(control_widget)

        # 创建选项卡控件
        tab_widget = QTabWidget()

        # 添加图像处理选项卡
        image_processing_widget = self.create_image_processing_panel()
        tab_widget.addTab(image_processing_widget, "图像处理")

        # 添加视频处理选项卡
        video_processing_widget = self.create_video_processing_panel()
        tab_widget.addTab(video_processing_widget, "视频处理")

        # 添加计算机视觉选项卡
        cv_processing_widget = self.create_cv_processing_panel()
        tab_widget.addTab(cv_processing_widget, "计算机视觉")

        # 添加到控制布局
        control_layout.addWidget(tab_widget)
        control_layout.addStretch()

        return control_widget

    def create_image_processing_panel(self):
        """创建图像处理控制面板"""
        panel = QWidget()
        layout = QVBoxLayout(panel)

        # 旋转控制
        rotate_group = QGroupBox("旋转")
        rotate_layout = QHBoxLayout(rotate_group)

        rotate_label = QLabel("角度:")
        rotate_spin = QSpinBox()
        rotate_spin.setRange(-180, 180)
        rotate_spin.setValue(0)
        rotate_spin.setSuffix("°")

        rotate_button = QPushButton("旋转")
        rotate_button.clicked.connect(lambda: self.rotate_image(rotate_spin.value()))

        rotate_layout.addWidget(rotate_label)
        rotate_layout.addWidget(rotate_spin)
        rotate_layout.addWidget(rotate_button)

        # 缩放控制
        scale_group = QGroupBox("缩放")
        scale_layout = QHBoxLayout(scale_group)

        scale_label = QLabel("比例:")
        scale_spin = QSpinBox()
        scale_spin.setRange(10, 500)
        scale_spin.setValue(100)
        scale_spin.setSuffix("%")

        scale_button = QPushButton("缩放")
        scale_button.clicked.connect(lambda: self.resize_image(scale_spin.value()))

        scale_layout.addWidget(scale_label)
        scale_layout.addWidget(scale_spin)
        scale_layout.addWidget(scale_button)

        # 裁剪控制
        crop_group = QGroupBox("裁剪")
        crop_layout = QVBoxLayout(crop_group)

        crop_button = QPushButton("开始裁剪")
        crop_button.clicked.connect(self.start_crop)

        confirm_crop_button = QPushButton("确认裁剪")
        confirm_crop_button.clicked.connect(self.confirm_crop)
        confirm_crop_button.setEnabled(False)

        cancel_crop_button = QPushButton("取消裁剪")
        cancel_crop_button.clicked.connect(self.cancel_crop)
        cancel_crop_button.setEnabled(False)

        crop_layout.addWidget(crop_button)
        crop_layout.addWidget(confirm_crop_button)
        crop_layout.addWidget(cancel_crop_button)

        # 亮度/对比度控制
        brightness_contrast_group = QGroupBox("亮度/对比度")
        brightness_contrast_layout = QGridLayout(brightness_contrast_group)

        brightness_label = QLabel("亮度:")
        brightness_slider = QSlider(Qt.Horizontal)
        brightness_slider.setRange(-100, 100)
        brightness_slider.setValue(0)

        contrast_label = QLabel("对比度:")
        contrast_slider = QSlider(Qt.Horizontal)
        contrast_slider.setRange(50, 200)
        contrast_slider.setValue(100)

        brightness_value_label = QLabel("0")
        contrast_value_label = QLabel("1.0")

        apply_bc_button = QPushButton("应用")
        apply_bc_button.clicked.connect(lambda: self.adjust_brightness_contrast(
            brightness_slider.value(), contrast_slider.value() / 100.0))

        brightness_slider.valueChanged.connect(lambda value: brightness_value_label.setText(str(value)))
        contrast_slider.valueChanged.connect(lambda value: contrast_value_label.setText(f"{value / 100.0:.1f}"))

        brightness_contrast_layout.addWidget(brightness_label, 0, 0)
        brightness_contrast_layout.addWidget(brightness_slider, 0, 1)
        brightness_contrast_layout.addWidget(brightness_value_label, 0, 2)

        brightness_contrast_layout.addWidget(contrast_label, 1, 0)
        brightness_contrast_layout.addWidget(contrast_slider, 1, 1)
        brightness_contrast_layout.addWidget(contrast_value_label, 1, 2)

        brightness_contrast_layout.addWidget(apply_bc_button, 2, 0, 1, 3)

        # 饱和度控制
        saturation_group = QGroupBox("饱和度")
        saturation_layout = QHBoxLayout(saturation_group)

        saturation_label = QLabel("饱和度:")
        saturation_slider = QSlider(Qt.Horizontal)
        saturation_slider.setRange(0, 200)
        saturation_slider.setValue(100)

        saturation_value_label = QLabel("1.0")
        apply_saturation_button = QPushButton("应用")
        apply_saturation_button.clicked.connect(lambda: self.adjust_saturation(saturation_slider.value() / 100.0))

        saturation_slider.valueChanged.connect(lambda value: saturation_value_label.setText(f"{value / 100.0:.1f}"))

        saturation_layout.addWidget(saturation_label)
        saturation_layout.addWidget(saturation_slider)
        saturation_layout.addWidget(saturation_value_label)
        saturation_layout.addWidget(apply_saturation_button)

        # 添加所有组到布局
        layout.addWidget(rotate_group)
        layout.addWidget(scale_group)
        layout.addWidget(crop_group)
        layout.addWidget(brightness_contrast_group)
        layout.addWidget(saturation_group)
        layout.addStretch()

        # 保存按钮引用以便后续启用/禁用
        self.confirm_crop_button = confirm_crop_button
        self.cancel_crop_button = cancel_crop_button

        return panel

    def create_video_processing_panel(self):
        """创建视频处理控制面板"""
        panel = QWidget()
        layout = QVBoxLayout(panel)

        # 视频控制
        video_group = QGroupBox("视频控制")
        video_layout = QHBoxLayout(video_group)

        play_button = QPushButton("播放")
        play_button.clicked.connect(self.play_video)

        pause_button = QPushButton("暂停")
        pause_button.clicked.connect(self.pause_video)

        stop_button = QPushButton("停止")
        stop_button.clicked.connect(self.stop_video)

        video_layout.addWidget(play_button)
        video_layout.addWidget(pause_button)
        video_layout.addWidget(stop_button)

        # 视频处理
        processing_group = QGroupBox("视频处理")
        processing_layout = QVBoxLayout(processing_group)

        processing_type_combo = QComboBox()
        processing_type_combo.addItems(["无处理", "人脸识别", "形状识别"])

        start_processing_button = QPushButton("开始处理")
        start_processing_button.clicked.connect(
            lambda: self.start_video_processing(processing_type_combo.currentText()))

        stop_processing_button = QPushButton("停止处理")
        stop_processing_button.clicked.connect(self.stop_video_processing)

        processing_layout.addWidget(processing_type_combo)
        processing_layout.addWidget(start_processing_button)
        processing_layout.addWidget(stop_processing_button)

        # 添加所有组到布局
        layout.addWidget(video_group)
        layout.addWidget(processing_group)
        layout.addStretch()

        # 保存按钮引用以便后续启用/禁用
        self.play_button = play_button
        self.pause_button = pause_button
        self.stop_button = stop_button

        return panel

    def create_cv_processing_panel(self):
        """创建计算机视觉控制面板"""
        panel = QWidget()
        layout = QVBoxLayout(panel)

        # 形状识别
        shape_detection_group = QGroupBox("形状识别")
        shape_detection_layout = QVBoxLayout(shape_detection_group)

        shape_detection_button = QPushButton("检测形状")
        shape_detection_button.clicked.connect(self.detect_shapes)

        shape_detection_layout.addWidget(shape_detection_button)

        # 人脸识别
        face_detection_group = QGroupBox("人脸识别")
        face_detection_layout = QVBoxLayout(face_detection_group)

        face_detection_button = QPushButton("检测人脸")
        face_detection_button.clicked.connect(self.detect_faces)

        face_detection_layout.addWidget(face_detection_button)

        # 图像拼接
        stitch_group = QGroupBox("图像拼接")
        stitch_layout = QVBoxLayout(stitch_group)

        add_image_button = QPushButton("添加图像")
        add_image_button.clicked.connect(self.add_image_for_stitching)

        stitch_button = QPushButton("开始拼接")
        stitch_button.clicked.connect(self.stitch_images)

        self.stitch_image_list = QLabel("已添加图像: 0")

        stitch_layout.addWidget(self.stitch_image_list)
        stitch_layout.addWidget(add_image_button)
        stitch_layout.addWidget(stitch_button)

        # 添加所有组到布局
        layout.addWidget(shape_detection_group)
        layout.addWidget(face_detection_group)
        layout.addWidget(stitch_group)
        layout.addStretch()

        # 初始化图像拼接列表
        self.images_for_stitching = []

        return panel

    def create_display_area(self):
        """创建显示区域"""
        display_widget = QWidget()
        display_layout = QVBoxLayout(display_widget)

        # 创建选项卡控件
        self.display_tab_widget = QTabWidget()

        # 图像显示区域
        self.image_view = ImageView()
        self.display_tab_widget.addTab(self.image_view, "图像")

        # 视频显示区域
        self.video_view = QLabel()
        self.video_view.setAlignment(Qt.AlignCenter)
        self.video_view.setText("未播放视频")
        self.display_tab_widget.addTab(self.video_view, "视频")

        # 结果显示区域
        self.result_view = QLabel()
        self.result_view.setAlignment(Qt.AlignCenter)
        self.result_view.setText("处理结果将显示在这里")
        self.display_tab_widget.addTab(self.result_view, "结果")

        # 添加到显示布局
        display_layout.addWidget(self.display_tab_widget)

        # 连接选项卡切换信号
        self.display_tab_widget.currentChanged.connect(self.on_tab_changed)

        return display_widget

    def connect_signals(self):
        """连接信号和槽"""
        # 图像显示区域鼠标事件
        self.image_view.mousePressEvent = self.on_image_mouse_press
        self.image_view.mouseMoveEvent = self.on_image_mouse_move
        self.image_view.mouseReleaseEvent = self.on_image_mouse_release

    def open_image(self):
        """打开图像文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif);;所有文件 (*)"
        )

        if file_path:
            if self.processor.load_image(file_path):
                self.update_image_display()
                self.statusBar().showMessage(f"已加载图像: {os.path.basename(file_path)}")
                self.current_tab = 'image'
                self.display_tab_widget.setCurrentIndex(0)
            else:
                QMessageBox.warning(self, "错误", "无法加载图像，请检查文件格式和路径。")

    def save_image(self):
        """保存图像文件"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "没有可保存的图像。")
            return

        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存图像", "", "PNG 文件 (*.png);;JPEG 文件 (*.jpg);;BMP 文件 (*.bmp);;所有文件 (*)"
        )

        if file_path:
            if self.processor.save_image(file_path):
                self.statusBar().showMessage(f"已保存图像: {os.path.basename(file_path)}")
            else:
                QMessageBox.warning(self, "错误", "无法保存图像。")

    def open_video(self):
        """打开视频文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开视频", "", "视频文件 (*.mp4 *.avi *.mov *.mkv);;所有文件 (*)"
        )

        if file_path:
            self.start_video_processor(file_path)
            self.statusBar().showMessage(f"已加载视频: {os.path.basename(file_path)}")
            self.current_tab = 'video'
            self.display_tab_widget.setCurrentIndex(1)

    def use_camera(self):
        """使用摄像头"""
        self.start_video_processor(0)  # 0表示默认摄像头
        self.statusBar().showMessage("已连接摄像头")
        self.current_tab = 'video'
        self.display_tab_widget.setCurrentIndex(1)

    def start_video_processor(self, source):
        """启动视频处理器"""
        # 如果已有视频处理器运行，先停止它
        if hasattr(self, 'video_processor') and self.video_processor.isRunning():
            self.video_processor.stop()
            self.video_processor.wait()

        # 创建新的视频处理器
        self.video_processor = VideoProcessor(source, self.processor)
        self.video_processor.frame_updated.connect(self.update_video_display)
        self.video_processor.error_occurred.connect(self.show_error)
        self.video_processor.processing_finished.connect(self.on_video_processing_finished)

        # 启动视频处理器
        self.video_processor.start()

        # 更新UI状态
        self.play_button.setEnabled(True)
        self.pause_button.setEnabled(False)
        self.stop_button.setEnabled(True)

    def play_video(self):
        """播放视频"""
        if hasattr(self, 'video_processor') and self.video_processor.isRunning():
            self.statusBar().showMessage("播放视频")
            self.play_button.setEnabled(False)
            self.pause_button.setEnabled(True)

    def pause_video(self):
        """暂停视频"""
        if hasattr(self, 'video_processor') and self.video_processor.isRunning():
            self.statusBar().showMessage("暂停视频")
            self.play_button.setEnabled(True)
            self.pause_button.setEnabled(False)

    def stop_video(self):
        """停止视频"""
        if hasattr(self, 'video_processor') and self.video_processor.isRunning():
            self.video_processor.stop()
            self.video_processor.wait()
            self.video_view.setText("未播放视频")
            self.statusBar().showMessage("视频已停止")
            self.play_button.setEnabled(True)
            self.pause_button.setEnabled(False)
            self.stop_button.setEnabled(False)

    def start_video_processing(self, processing_type):
        """开始视频处理"""
        if not hasattr(self, 'video_processor') or not self.video_processor.isRunning():
            QMessageBox.warning(self, "错误", "请先打开视频或连接摄像头。")
            return

        if processing_type == "无处理":
            self.video_processor.stop_processing()
        elif processing_type == "人脸识别":
            self.video_processor.start_processing('face')
        elif processing_type == "形状识别":
            self.video_processor.start_processing('shape')

        self.statusBar().showMessage(f"正在进行{processing_type}")

    def stop_video_processing(self):
        """停止视频处理"""
        if hasattr(self, 'video_processor') and self.video_processor.isRunning():
            self.video_processor.stop_processing()
            self.statusBar().showMessage("已停止视频处理")

    def on_video_processing_finished(self):
        """视频处理完成回调"""
        self.statusBar().showMessage("视频处理已完成")

    def rotate_image(self, angle):
        """旋转图像"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "请先加载图像。")
            return

        self.processor.rotate_image(angle)
        self.update_image_display()
        self.statusBar().showMessage(f"已旋转图像 {angle} 度")

    def resize_image(self, scale):
        """缩放图像"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "请先加载图像。")
            return

        self.processor.resize_image(scale)
        self.update_image_display()
        self.statusBar().showMessage(f"已缩放图像至 {scale}%")

    def start_crop(self):
        """开始裁剪图像"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "请先加载图像。")
            return

        self.is_cropping = True
        self.crop_start = None
        self.crop_end = None
        self.confirm_crop_button.setEnabled(False)
        self.cancel_crop_button.setEnabled(True)
        self.statusBar().showMessage("请在图像上拖动选择裁剪区域")

    def confirm_crop(self):
        """确认裁剪图像"""
        if self.is_cropping and self.crop_start and self.crop_end:
            # 确保起点坐标小于终点坐标
            x1, y1 = min(self.crop_start.x(), self.crop_end.x()), min(self.crop_start.y(), self.crop_end.y())
            x2, y2 = max(self.crop_start.x(), self.crop_end.x()), max(self.crop_start.y(), self.crop_end.y())

            # 计算裁剪区域
            rect = (x1, y1, x2 - x1, y2 - y1)

            # 执行裁剪
            self.processor.crop_image(rect)
            self.update_image_display()

            # 重置裁剪状态
            self.is_cropping = False
            self.crop_start = None
            self.crop_end = None
            self.confirm_crop_button.setEnabled(False)
            self.cancel_crop_button.setEnabled(False)
            self.statusBar().showMessage("已裁剪图像")

    def cancel_crop(self):
        """取消裁剪图像"""
        self.is_cropping = False
        self.crop_start = None
        self.crop_end = None
        self.confirm_crop_button.setEnabled(False)
        self.cancel_crop_button.setEnabled(False)
        self.update_image_display()  # 刷新图像以移除裁剪框
        self.statusBar().showMessage("已取消裁剪")

    def adjust_brightness_contrast(self, brightness, contrast):
        """调整亮度和对比度"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "请先加载图像。")
            return

        self.processor.adjust_brightness_contrast(brightness, contrast)
        self.update_image_display()
        self.statusBar().showMessage(f"已调整亮度: {brightness}, 对比度: {contrast:.1f}")

    def adjust_saturation(self, saturation):
        """调整饱和度"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "请先加载图像。")
            return

        self.processor.adjust_saturation(saturation)
        self.update_image_display()
        self.statusBar().showMessage(f"已调整饱和度: {saturation:.1f}")

    def detect_shapes(self):
        """检测图像中的形状"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "请先加载图像。")
            return

        shapes = self.processor.detect_shapes()
        self.update_image_display()
        self.statusBar().showMessage(f"已检测到 {len(shapes)} 个形状")

        # 在结果面板显示形状统计信息
        self.show_shape_statistics(shapes)

    def detect_faces(self):
        """检测图像中的人脸"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "错误", "请先加载图像。")
            return

        faces = self.processor.detect_faces()
        self.update_image_display()
        self.statusBar().showMessage(f"已检测到 {len(faces)} 个人脸")

        # 在结果面板显示人脸统计信息
        self.show_face_statistics(faces)

    def add_image_for_stitching(self):
        """添加图像用于拼接"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "添加图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp);;所有文件 (*)"
        )

        if file_path:
            image = cv2.imread(file_path)
            if image is not None:
                self.images_for_stitching.append(image)
                self.stitch_image_list.setText(f"已添加图像: {len(self.images_for_stitching)}")
                self.statusBar().showMessage(f"已添加图像: {os.path.basename(file_path)}")
            else:
                QMessageBox.warning(self, "错误", "无法加载图像，请检查文件格式和路径。")

    def stitch_images(self):
        """拼接图像"""
        if len(self.images_for_stitching) < 2:
            QMessageBox.warning(self, "错误", "至少需要添加两张图像进行拼接。")
            return

        if self.processor.stitch_images(self.images_for_stitching):
            self.update_image_display()
            self.statusBar().showMessage("图像拼接成功")

            # 清空图像列表
            self.images_for_stitching = []
            self.stitch_image_list.setText(f"已添加图像: {len(self.images_for_stitching)}")
        else:
            QMessageBox.warning(self, "错误", "图像拼接失败，请确保图像有足够的重叠区域。")

    def undo(self):
        """撤销上一步操作"""
        if self.processor.undo():
            self.update_image_display()
            self.statusBar().showMessage("已撤销操作")
        else:
            self.statusBar().showMessage("无法撤销")

    def redo(self):
        """重做上一步操作"""
        if self.processor.redo():
            self.update_image_display()
            self.statusBar().showMessage("已重做操作")
        else:
            self.statusBar().showMessage("无法重做")

    def reset_zoom(self):
        """重置图像缩放"""
        if self.processor.current_image is not None:
            self.image_view.scale_factor = 1.0
            self.image_view.image_label.resize(self.processor.current_image.shape[1],
                                               self.processor.current_image.shape[0])

    def show_about(self):
        """显示关于对话框"""
        QMessageBox.about(self, "关于", "计算机视觉开发平台\n\n"
                                      "版本: 1.0\n"
                                      "一个基于Python和OpenCV的计算机视觉开发平台，支持图像处理、人脸识别和视频分析等功能。")

    def update_image_display(self):
        """更新图像显示"""
        if self.processor.current_image is not None:
            # 将OpenCV的BGR格式转换为RGB
            rgb_image = cv2.cvtColor(self.processor.current_image, cv2.COLOR_BGR2RGB)

            # 创建QImage
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)

            # 如果正在裁剪，绘制裁剪框
            if self.is_cropping and self.crop_start and self.crop_end:
                painter = QPainter(q_image)
                painter.setPen(QPen(Qt.red, 2, Qt.DashLine))

                # 确保起点坐标小于终点坐标
                x1, y1 = min(self.crop_start.x(), self.crop_end.x()), min(self.crop_start.y(), self.crop_end.y())
                x2, y2 = max(self.crop_start.x(), self.crop_end.x()), max(self.crop_start.y(), self.crop_end.y())

                painter.drawRect(x1, y1, x2 - x1, y2 - y1)
                painter.end()

            # 设置图像
            pixmap = QPixmap.fromImage(q_image)
            self.image_view.set_image(pixmap)

    def update_video_display(self, frame):
        """更新视频显示"""
        # 将OpenCV的BGR格式转换为RGB
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # 创建QImage
        h, w, ch = rgb_frame.shape
        bytes_per_line = ch * w
        q_image = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)

        # 设置图像
        pixmap = QPixmap.fromImage(q_image)
        self.video_view.setPixmap(pixmap.scaled(
            self.video_view.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def show_shape_statistics(self, shapes):
        """显示形状统计信息"""
        if not shapes:
            self.result_view.setText("未检测到形状")
            return

        # 统计各种形状的数量
        shape_counts = {}
        for shape in shapes:
            name = shape['name']
            shape_counts[name] = shape_counts.get(name, 0) + 1

        # 创建统计信息文本
        stats_text = "形状识别统计:\n\n"
        for name, count in shape_counts.items():
            stats_text += f"{name}: {count}个\n"

        # 在结果面板显示统计信息
        self.result_view.setText(stats_text)
        self.display_tab_widget.setCurrentIndex(2)

    def show_face_statistics(self, faces):
        """显示人脸统计信息"""
        if not faces:
            self.result_view.setText("未检测到人脸")
            return

        # 创建统计信息文本
        stats_text = f"人脸识别统计:\n\n共检测到 {len(faces)} 个人脸\n\n"

        # 在结果面板显示统计信息
        self.result_view.setText(stats_text)
        self.display_tab_widget.setCurrentIndex(2)

    def on_tab_changed(self, index):
        """选项卡切换事件"""
        """选项卡切换事件"""
        tab_text = self.display_tab_widget.tabText(index)
        self.current_tab = tab_text.lower()
        self.statusBar().showMessage(f"已切换到 {tab_text} 视图")

    def on_image_mouse_press(self, event):
        """图像区域鼠标按下事件"""
        if self.is_cropping and event.button() == Qt.LeftButton:
            # 获取鼠标在图像上的位置
            pos = event.pos()
            self.crop_start = pos
            self.crop_end = pos
            self.update_image_display()  # 立即更新显示以显示起始点

    def on_image_mouse_move(self, event):
        """图像区域鼠标移动事件"""
        if self.is_cropping and self.crop_start and event.buttons() & Qt.LeftButton:
            # 获取鼠标在图像上的位置
            pos = event.pos()
            self.crop_end = pos
            self.update_image_display()  # 更新显示以显示当前裁剪框

    def on_image_mouse_release(self, event):
        """图像区域鼠标释放事件"""
        if self.is_cropping and self.crop_start and event.button() == Qt.LeftButton:
            # 获取鼠标在图像上的位置
            pos = event.pos()
            self.crop_end = pos

            # 计算裁剪区域大小
            width = abs(self.crop_end.x() - self.crop_start.x())
            height = abs(self.crop_end.y() - self.crop_start.y())

            # 如果裁剪区域足够大，启用确认按钮
            if width > 5 and height > 5:
                self.confirm_crop_button.setEnabled(True)
            else:
                self.confirm_crop_button.setEnabled(False)

            self.update_image_display()  # 更新显示以显示最终裁剪框

    def show_error(self, message):
        """显示错误消息"""
        QMessageBox.warning(self, "错误", message)


if __name__ == "__main__":
    # 确保中文显示正常
    os.environ["QT_FONT_DPI"] = "96"

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())